package com.xiaoyao.novel.util;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.sun.net.ssl.HostnameVerifier;
import com.sun.net.ssl.HttpsURLConnection;

/**
 * HTTP 操作类. <BR>
 * 可以加上传参 编码的接口.<BR>
 * cookie 使用上一次返回的cookie<BR>
 * 
 * @author 旷明爱
 * @date Aug 28, 2012 2:25:19 PM
 */
public class HttpUtil {
	private static Logger logger = Logger.getLogger(HttpUtil.class);
	// 超时时间 20 s
	private int TIMEOUT = 20 * 1000;
	private static HttpUtil httpUtil = new HttpUtil("UTF-8");

	private String charset = "UTF-8";
	public String cookie = "";

	public void setTIMEOUT(int timeout) {
		TIMEOUT = timeout;
	}

	/**
	 * 默认编码
	 * 
	 * @return
	 */
	public static HttpUtil getDefault() {
		return httpUtil;
	}

	public HttpUtil() { 
	}
	/**
	 * 可以指定编码
	 * 
	 * @param charset
	 */
	public HttpUtil(String charset) {
		this.charset = charset;
	}

	// *****************************************************
	static {
		try {
			trustAllHttpsCertificates();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("Https 初始化失败!");
			throw new RuntimeException(e);
		}
	}

	/**
	 * GET HttpURLConnection <BR>
	 * 限制访问间隔.
	 * 
	 * @param urlNode
	 * @param param
	 * @return
	 * @throws IOException
	 */
	public HttpURLConnection getGetConnection(String urlStr, Map<String, String> headMap) throws IOException {
		URL url = new URL(urlStr);// 
		HttpURLConnection con = null;
		con = (HttpURLConnection) url.openConnection();
		// if (userProxy) {
		// // 201.85.50.249:80
		// // InetAddress address = InetAddress.getByAddress(new byte[] { (byte)
		// 201, (byte) 85, (byte) 50, (byte) 249 });
		// // Proxy proxy = new Proxy(Proxy.Type.HTTP, new
		// InetSocketAddress(address, 80));
		// // con = (HttpURLConnection) url.openConnection(proxy);
		//
		// con.setRequestProperty("x-forwarded-for", "201.85.50.249");
		// }
		con.setRequestProperty("User-Agent",
		// IE
				// "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1;
				// Trident/4.0; InfoPath.2; .NET CLR 2.0.50727; .NET CLR
				// 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)"); 
		// "Mozilla/5.0 (Windows NT 6.1; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1");
		"Baiduspider");
		con.setRequestProperty("Cookie", cookie);

		con.setConnectTimeout(TIMEOUT);
		con.setReadTimeout(2 * TIMEOUT);
		con.setInstanceFollowRedirects(!false);// TODO 设置为false 即为不自动跳转.
		if (headMap == null) {
			return con;
		}
		for (Map.Entry<String, String> entry : headMap.entrySet()) {
			con.setRequestProperty(entry.getKey(), entry.getValue());
		}
		return con;
	}

	/**
	 * POST HttpURLConnection
	 * 
	 * @param urlNode
	 * @param param
	 * @param headMap
	 * @return
	 * @throws IOException
	 */
	public HttpURLConnection getPostConnection(String urlStr, String param, Map<String, String> headMap) throws IOException {
		HttpURLConnection con = getGetConnection(urlStr, headMap);
		con.setRequestMethod("POST");// 
		con.setDoOutput(true);
		return con;
	}
	/**
	 * 格式化参数列表
	 * 
	 * @param paramMap
	 * @return
	 */
	public String formatParam(Map<String, String> paramMap) {
		if (paramMap.isEmpty()) {
			return "";
		}
		StringBuffer buffer = new StringBuffer();
		Set<String> keySet = paramMap.keySet();
		for (String key : keySet) {
			String value = paramMap.get(key);
			buffer.append("&").append(key).append("=").append(value);
		}
		return buffer.replace(0, 1, "").toString();
	}

	/**
	 * 通过拼接的方式构造请求内容，实现参数传输以及文件传输
	 * 
	 * @param actionUrl
	 * @param params
	 * @param files
	 * @return
	 * @throws IOException
	 */
	public String post(String actionUrl, Map<String, String> params, Map<String, File> files) throws IOException {
		String BOUNDARY = java.util.UUID.randomUUID().toString();
		BOUNDARY="7dd2c9261045e";
		String PREFIX = "-----------------------------", LINEND = "\r\n", SURFIX = "--";
		String MULTIPART_FROM_DATA = "multipart/form-data";
		String charset = "GBK";
		StringBuilder sb2 = new StringBuilder();

		URL url = new URL(actionUrl);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setRequestProperty("Cookie", cookie);
		conn.setReadTimeout(5 * 1000); // 缓存的最长时间
		conn.setDoInput(true);// 允许输入
		conn.setDoOutput(true);// 允许输出
		conn.setUseCaches(false); // 不允许使用缓存
		conn.setRequestMethod("POST");
		conn.setRequestProperty("connection", "keep-alive");
		conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + PREFIX + BOUNDARY);

		// 首先组拼文本类型的参数
		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, String> entry : params.entrySet()) {
			sb.append(PREFIX);
			sb.append(BOUNDARY);
			sb.append(LINEND);
			sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND);
			// sb.append("Content-Type: text/plain; charset=" + CHARSET +
			// LINEND);
			// sb.append("Content-Transfer-Encoding: 8bit" + LINEND);
			sb.append(LINEND);
			sb.append(entry.getValue()).append(LINEND);
		}
		System.out.print(sb);
		DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
		outStream.write(sb.toString().getBytes(charset));
		InputStream in = null;
		// 发送文件数据
		if (files != null) {
			for (Map.Entry<String, File> fileEntry : files.entrySet()) {
				StringBuilder sb1 = new StringBuilder();
				sb1.append(PREFIX);
				sb1.append(BOUNDARY);
				sb1.append(LINEND);
				sb1.append("Content-Disposition: form-data; name=\"litpic\"; filename=\"\"" + LINEND);
				sb1.append("Content-Type: application/octet-stream;" + LINEND);
				sb1.append(LINEND);
				outStream.write(sb1.toString().getBytes(charset));
				if (fileEntry.getValue() != null) {
					InputStream is = new FileInputStream(fileEntry.getValue());
					byte[] buffer = new byte[1024];
					int len = 0;
					while ((len = is.read(buffer)) != -1) {
						outStream.write(buffer, 0, len);
					}
					is.close();
				} else {
					outStream.write("".getBytes(charset));
				}
				outStream.write(LINEND.getBytes(charset));
			}

			// 请求结束标志
			byte[] end_data = (PREFIX + BOUNDARY + SURFIX + LINEND).getBytes(charset);
			outStream.write(end_data);
			outStream.flush();
			// 得到响应码
			int res = conn.getResponseCode();
			if (res == 200) {
				in = conn.getInputStream();
				BufferedReader br = new BufferedReader(new InputStreamReader(in));
				String line = null;
				while ((line = br.readLine()) != null) {
					sb2.append(line);
				}
			}
			outStream.close();
			conn.disconnect();
		}
		return sb2.toString();
	}

	/**
	 * 从 URLConnection 中提取 cookie.
	 * 
	 * @param connection
	 * @return
	 */
	Map<String, String> cookieMap = new HashMap<String, String>();//

	/**
	 * 从 URLConnection 中提取 cookie.
	 * 
	 * @param connection
	 * @return
	 */
	public String getCookie(URLConnection connection) {
		// 方法 TODO有问题..
		Map<String, List<String>> map = connection.getHeaderFields();
		logger.debug("HeaderFields :" + map);
		List<String> list = map.get("Set-Cookie");
		String newCookie = cookie; 
		if (list == null || list.size() == 0) {
			return cookie;
		}
		for (int i = 0; i < list.size(); i++) {
			String line = list.get(i).replaceFirst(";.+", "");
			if (line.contains("PHPSESSID")) {
				newCookie=line;
			}
		} 
		logger.debug("newCookie :" + newCookie);
		cookie=newCookie;
		return newCookie;
	}

	/**
	 * 从连接中读取数据
	 * 
	 * @param is
	 * @return
	 * @throws IOException
	 */
	public String getConData(HttpURLConnection con, String param) throws IOException {
		// logger.debug("马上连接......");
		con.connect();
		// POST 方式
		if (!ClassUtil.isEmpty(param)) {
			con.getOutputStream().write(param.getBytes(charset)); // 要指定编码
		}
		cookie = getCookie(con);// 获取cookie,刷新session
		// ************************************************
		// 跳转
		// int code = ((HttpURLConnection) con).getResponseCode();
		// System.out.println("code:" + code);
		// Map<String, List<String>> map = con.getHeaderFields();

		String location = con.getHeaderField("Location");
		if (!ClassUtil.isEmpty(location)) {
			String baseUrl = con.getURL().toString();
			baseUrl = baseUrl.replaceFirst("/[^/]+$", "/");
			location = baseUrl + location;
			logger.info("跳转到:" + location);
			HttpURLConnection cc = getGetConnection(location, null);
			return getConData(cc, null);
		}
		// **********************************************
		String line = "";
		StringBuffer buffer = new StringBuffer(10000);
		BufferedReader reader = null;
		InputStream is = null;
		try {
			is = con.getInputStream();
			reader = new BufferedReader(new InputStreamReader(is, charset));
			while ((line = reader.readLine()) != null) {
				buffer.append(line);
			}
		} catch (IOException e) {
			logger.error("获取页面失败!" + e.getMessage());
			throw e;
		} finally {
			if (reader != null) {
				reader.close();
				is.close();
			}
			con.disconnect();
		}
		return buffer.toString() + "";
	}

	/**
	 * Get 方式获取页面
	 * 
	 * @param urlStr
	 * @return
	 * @throws Exception
	 */
	public String getPage(String urlStr) throws IOException {
		return getPage(urlStr, null);
	}

	/**
	 * Get 方式获取页面,可以设置头部
	 * 
	 * @param urlStr
	 * @return
	 * @throws Exception
	 */
	public String getPage(String urlStr, Map<String, String> headMap) throws IOException {
		HttpURLConnection con = getGetConnection(urlStr, headMap);
		return getConData(con, null);
	}

	/**
	 * Post 方式获取页面,可以设置头部
	 * 
	 * @param urlStr
	 * @param param
	 * @return
	 * @throws Exception
	 */
	public String postPage(String urlStr, String param, Map<String, String> headMap) throws IOException {
		HttpURLConnection con = getPostConnection(urlStr, param, headMap);
		return getConData(con, param);
	}

	/**
	 * Post 方式获取页面
	 * 
	 * @param urlStr
	 * @param param
	 * @return
	 * @throws Exception
	 */
	public String postPage(String urlStr, String param) throws IOException {
		return postPage(urlStr, param, null);
	}
 

	/**
	 * HTTPS 证书
	 * 
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static void trustAllHttpsCertificates() throws NoSuchAlgorithmException, KeyManagementException {
		javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
		javax.net.ssl.TrustManager tm = new miTM();
		trustAllCerts[0] = tm;
//		javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("TLS"); // SSL
		javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("TLS"); // SSL
		sc.init(null, trustAllCerts, null);
		
		javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
//		SSLSocketFactory.getSocketFactory().setHostnameVerifier(new AllowAllHostnameVerifier());   
		

	    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());  
	    HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
			
			public boolean verify(String arg0, String arg1) { 
				return false;
			}
		}); 
	}

	static class miTM implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {
		public java.security.cert.X509Certificate[] getAcceptedIssuers() {
			return null;
		}

		public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
			return true;
		}

		public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
			return true;
		}

		public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
			return;
		}

		public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
			return;
		}
	}

	/**
	 * 下载图片
	 * 
	 * @param picStr
	 * @return byte[] 下载失败返回null
	 */
	public byte[] getPic(String picStr) {
		try {
			HttpURLConnection con = getGetConnection(picStr, null);
			InputStream is = con.getInputStream();

			byte[] cbuf = new byte[1024000];// 最大约1M ,
			int read = 0;
			int leng = 0;
			int perByte = 1000;
			while ((read = is.read(cbuf, leng, perByte)) > 0) {
				leng += read;
			}
			byte[] realByte = new byte[leng];
			System.arraycopy(cbuf, 0, realByte, 0, leng);
			return realByte;
		} catch (Exception e) {
			logger.error("下载图片失败!" + e.getMessage());
		}
		return null;
	}

}
